package xj.graph2d.app;

import static xj.app.BaseApplicationFrame.getResourceString;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.*;
import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.io.InputStream; 

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.jdesktop.swingx.JXStatusBar;
import org.jdesktop.swingx.JXTitledPanel;
import org.jdesktop.swingx.border.DropShadowBorder;
import org.jdesktop.swingx.plaf.basic.BasicStatusBarUI;

import xj.app.BaseApplication;
import xj.app.BaseApplicationFrame;
import xj.app.ClosablePanel;
import xj.graph2d.Document;
import xj.graph2d.SimpleDrawDocument;
import xj.graph2d.SimpleDrawView;

public abstract class Main   
  extends BaseApplication {

  protected static final int UPPER_PANEL  = 0;
  protected static final int LOWER_PANEL  = 1;

  protected static final int LEFT_PANEL   = 0;
  protected static final int CENTER_PANEL = 1;
  protected static final int RIGHT_PANEL  = 2;

  protected JComponent initPanel(String name) {
    return null;
  }

  protected void setUpContentPane(SimpleDrawView view, boolean useTabbedPane) {
    initPanels(view, useTabbedPane);
    arrangePanels(); 
    resetDockingPanel();
  }

  protected void initPanels(SimpleDrawView view, boolean useTabbedPane) { 
    for (ClosablePanel cp : closablePanelMap.values()) { 
      String name = cp.getName(); 
      JComponent comp = initPanel(name);    
      if (titledPanels) { 
	String title = getResourceString(name + ".Panel.title");
	if (title == null) { 
	  title = name;
	}
	JXTitledPanel panel = new JXTitledPanel(title);
	if (comp != null) {
	  panel.setContentContainer(comp);

	  Icon icon = cp.getIcon();
	  if (icon != null) { 
	    panel.setLeftDecoration(new JLabel(icon));
	  }

	  //System.out.println("Main.initPanels() " + cp.getName() + " closable=" + cp.isClosable());

	  JComponent[] deco = null;
	  if (comp instanceof DecoratedComponent) { 
	    DecoratedComponent dcomp = (DecoratedComponent) comp;
	    deco = dcomp.makeTitleDeco();
	  }

	  if (deco != null || 
	      cp.isClosable() ||
	      cp.isDetachable()) { 
	    JPanel decoPanel = new JPanel();
	    decoPanel.setLayout(new BoxLayout(decoPanel, BoxLayout.X_AXIS));
	    if (deco != null) { 
	      for (int i = 0; i < deco.length; i++) { 
		decoPanel.add(deco[i]);
	      }
	    }
	    if (cp.isClosable()) { 
	      Icon closeViewIcon = getIcon("ViewMinIcon"); 
	      if (closeViewIcon != null) {
		JButton minButton = new JButton(closeViewIcon);
		//minButton.setBorder(null);
		minButton.setToolTipText("minimize view");
		minButton.setActionCommand(cp.getName());
		minButton.addActionListener(new ActionListener() {
		    public void actionPerformed(ActionEvent evt) { 
		      if (evt != null) { 
			JButton b = (JButton) evt.getSource(); 
			String cmd = b.getActionCommand();
			closeClosablePanel(cmd);		      
		      }
		    }
		  });
		decoPanel.add(minButton); 
	      }
	    }

	    if (cp.isDetachable()) { 
	      Icon maxViewIcon = getIcon("ViewMaxIcon"); 
	      if (maxViewIcon != null) {
		JButton maxButton = new JButton(maxViewIcon);
		maxButton.setToolTipText("detatch view");
		maxButton.setActionCommand(cp.getName());
		maxButton.addActionListener(new ActionListener() {
		    public void actionPerformed(ActionEvent evt) { 
		      if (evt != null) { 
			JButton b = (JButton) evt.getSource(); 
			String cmd = b.getActionCommand();
			detachClosablePanel(cmd);		      
		      }
		    }
		  });
		decoPanel.add(maxButton); 
	      }
	    }

	    panel.setRightDecoration(decoPanel); 
	  }
	  
	  if (shadowPanels) { 
	    DropShadowBorder dsb = new DropShadowBorder();
	    panel.setBorder(dsb);
	  }
	  cp.setComponent(panel);
	}
      } else { 
	cp.setComponent(comp);
      }
    }

    if (view != null) {
      JComponent center = null; 
      if (useTabbedPane) {
	tabbedPane = new JTabbedPane(SwingConstants.TOP);
	initTabbedPaneUI(tabbedPane);
	tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); 
	//tabbedPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); 
	tabbedPane.addChangeListener(new TabbedPaneListener());
	tabbedPane.addTab(view.getDocumentName(), 
			  getDocumentIcon(view.getDocument()), view);
	center = tabbedPane;
      } else {
	center = view;
      }
      canvasPanel = makeCanvasPanel(center);      
    }
  }

  public void arrangePanels() {
    JComponent panels[][] = new JComponent[2][3];
    for (ClosablePanel cp : closablePanelMap.values()) { 
      if (!cp.isClosed()) { 
	int vpos = cp.getVPosition();
	int hpos = cp.getHPosition();
	if (vpos >= 0 && vpos <= 1 && 
	    hpos >= 0 && hpos <= 2) {
	  panels[vpos][hpos] = cp.getComponent();
	}
      }
    }
    panels[UPPER_PANEL][CENTER_PANEL] = canvasPanel; 
    makeTopPane(panels);
  }

  protected Icon getDocumentIcon(Document document) {
    return null;
  }

  protected void makeTopPane(JComponent[][] panels) { 
    JComponent center = panels[UPPER_PANEL][CENTER_PANEL];
    if (panels[LOWER_PANEL][CENTER_PANEL] != null) {
      JSplitPane centerArea = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
      centerArea.setTopComponent(panels[UPPER_PANEL][CENTER_PANEL]);
      centerArea.setBottomComponent(panels[LOWER_PANEL][CENTER_PANEL]);
      centerArea.setResizeWeight(1);
      
      centerArea.setDividerLocation(upperPanelHeight[CENTER_PANEL]);
      if (dividerSize > 0) {
	centerArea.setDividerSize(dividerSize);
      }
      center = centerArea;
    }
    
    JComponent centerRight = center;
    boolean hasRightPanel = (panels[UPPER_PANEL][RIGHT_PANEL] != null || 
			     panels[LOWER_PANEL][RIGHT_PANEL] != null);
    boolean hasLeftPanel = (panels[UPPER_PANEL][LEFT_PANEL] != null || 
			    panels[LOWER_PANEL][LEFT_PANEL] != null);

    if (hasRightPanel) {
      if (panels[UPPER_PANEL][RIGHT_PANEL] == null) {
	panels[UPPER_PANEL][RIGHT_PANEL] = panels[LOWER_PANEL][RIGHT_PANEL];
	panels[LOWER_PANEL][RIGHT_PANEL] = null;
      }
      
      JComponent right = panels[UPPER_PANEL][RIGHT_PANEL];
      if (panels[LOWER_PANEL][RIGHT_PANEL] != null) {
	JSplitPane rightArea = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
	rightArea.setTopComponent(panels[UPPER_PANEL][RIGHT_PANEL]);
	rightArea.setBottomComponent(panels[LOWER_PANEL][RIGHT_PANEL]);
	rightArea.setResizeWeight(0.5);
	
	rightArea.setDividerLocation(upperPanelHeight[RIGHT_PANEL]);
	if (dividerSize > 0) {
	  rightArea.setDividerSize(dividerSize);
	}
	right = rightArea;
      }
      
      JSplitPane centerRightArea = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
      centerRightArea.setResizeWeight(1);
      centerRightArea.setLeftComponent(center);
      centerRightArea.setRightComponent(right);
      
      if (hasLeftPanel) {
	centerRightArea.setDividerLocation(panelWidth[CENTER_PANEL]);
      } else { 
	centerRightArea.setDividerLocation(panelWidth[LEFT_PANEL] + panelWidth[CENTER_PANEL]);
      }
      if (dividerSize > 0) {
	centerRightArea.setDividerSize(dividerSize);
      }
      centerRight = centerRightArea;
    }
    
    top = centerRight;
    if (hasLeftPanel) {
      if (panels[UPPER_PANEL][LEFT_PANEL] == null) {
	panels[UPPER_PANEL][LEFT_PANEL] = panels[LOWER_PANEL][LEFT_PANEL];
	panels[LOWER_PANEL][LEFT_PANEL] = null;
      }
      
      JComponent left = panels[UPPER_PANEL][LEFT_PANEL];
      if (panels[LOWER_PANEL][LEFT_PANEL] != null) {
	JSplitPane leftArea = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
	leftArea.setTopComponent(panels[UPPER_PANEL][LEFT_PANEL]);
	leftArea.setBottomComponent(panels[LOWER_PANEL][LEFT_PANEL]);
	leftArea.setResizeWeight(0.5);
	
	leftArea.setDividerLocation(upperPanelHeight[LEFT_PANEL]);
	if (dividerSize > 0) {
	  leftArea.setDividerSize(dividerSize);
	}
	left = leftArea;
      }
      
      JSplitPane topPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
      topPane.setRightComponent(centerRight);
      topPane.setLeftComponent(left);
      
      topPane.setDividerLocation(panelWidth[LEFT_PANEL]);
      if (dividerSize > 0) {
	topPane.setDividerSize(dividerSize);
      }
      top = topPane;
    }
  }

  protected void initTabbedPaneUI(JTabbedPane tabbedPane) {}

  public void createStatusBar() {
    statusBar = new JXStatusBar();
    statusBar.putClientProperty(BasicStatusBarUI.AUTO_ADD_SEPARATOR, false);

    if (statusBarLeft != null && 
	statusBarLeft.length > 0) { 
      for (int i = 0; i < statusBarLeft.length; i++) { 
	statusBar.add(statusBarLeft[i]);
	statusBar.add(Box.createHorizontalStrut(6));
      }
      statusBar.add(new JSeparator(SwingConstants.VERTICAL));
      statusBar.add(Box.createHorizontalStrut(6));
    }

    messageLabel = new JLabel();   
    JXStatusBar.Constraint c1 = 
      new JXStatusBar.Constraint(JXStatusBar.Constraint.ResizeBehavior.FILL);
    statusBar.add(messageLabel, c1);

    statusBar.add(Box.createHorizontalGlue());

    statusBar.add(new JSeparator(SwingConstants.VERTICAL));    
    statusBar.add(dockingPanel);

    if (statusBarRight != null) { 
      statusBar.add(new JSeparator(SwingConstants.VERTICAL));
      JXStatusBar.Constraint c2 = new JXStatusBar.Constraint();
      c2.setFixedWidth(100);
      statusBar.add(statusBarRight, c2);
    }
  }

  public void addClosablePanel(ClosablePanel panel) { 
    if (panel != null && 
	panel.getName() != null) { 
      closablePanelMap.put(panel.getName(), panel);
    }
  }

  protected JComponent makeCanvasPanel(JComponent comp) { 
    if (comp != null && 
	titledPanels) { 
      String title = getResourceString("Canvas.Panel.title");
      if (title == null) { 
	title = "Canvas";
      }
      JXTitledPanel panel = new JXTitledPanel(title, comp);

      Icon icon = getIcon("CanvasIcon"); 
      if (icon != null) { 
	panel.setLeftDecoration(new JLabel(icon));
      }
      if (shadowPanels) { 
	DropShadowBorder dsb = new DropShadowBorder();
	panel.setBorder(dsb);
      }

      return panel; 
    }
    return comp;
  }

  protected void setCanvasTitleDecoration(JComponent deco) { 
    if (deco != null && 
	canvasPanel instanceof JXTitledPanel) {
      JXTitledPanel tpanel = (JXTitledPanel) canvasPanel; 
      tpanel.setRightDecoration(deco);
    }
  }

  public void addViewTab(SimpleDrawView view2) {
    addViewTab(view2, null);
  }

  public void addViewTab(SimpleDrawView view2, Icon icon) {
    if (view2 != null) {
      JComponent workarea = getWorkArea();
      if (tabbedPane != null && workarea == tabbedPane) {
	tabbedPane.addTab(view2.getDocumentName(), icon, view2);
      } else {
	if (tabbedPane == null) {
	  tabbedPane = new JTabbedPane(SwingConstants.TOP);
	  tabbedPane.addChangeListener(new TabbedPaneListener());
	}
	if (workarea instanceof SimpleDrawView) {
	  SimpleDrawView view1 = (SimpleDrawView) workarea;
	  tabbedPane.add(view1.getDocumentName(), view1);
	}
	tabbedPane.addTab(view2.getDocumentName(), icon, view2);

	canvasPanel = makeCanvasPanel(tabbedPane);   
	arrangePanels();
      }
      tabbedPane.setSelectedComponent(view2);
      view2.setDrawToolkit(toolkit);
      view2.activateView();
    }

  }

  public int findViewTab(String name) {
    if (name != null && tabbedPane != null) {
      int n = tabbedPane.getTabCount();
      for (int i = 0; i < n; i++) {
	String title = tabbedPane.getTitleAt(i);
	if (name.equals(title)) {
	  return i;
	}
      }
    }
    return -1;
  }

  public int findViewTab(SimpleDrawDocument doc) {
    if (doc != null && tabbedPane != null) {
      int n = tabbedPane.getTabCount();
      for (int i = 0; i < n; i++) {
	Component comp = tabbedPane.getComponentAt(i);
	if (comp instanceof SimpleDrawView) {
	  SimpleDrawView v = (SimpleDrawView) comp;
	  SimpleDrawDocument d = v.getSimpleDrawDocument();
	  if (doc == d) {
	    return i;
	  }
	}
      }
    }
    return -1;
  }

  abstract protected void setFrameTitle();

  public void showViewTab(String name) {
    if (tabbedPane != null) {
      int i = findViewTab(name);
      if (i >= 0) { 
	showViewTab(i);
      }
    }
  }

  public void showViewTab(int i) {
    if (tabbedPane != null && 
	i >= 0 &&
	i < tabbedPane.getTabCount()) {
      Component comp = tabbedPane.getComponentAt(i);
      if (comp instanceof SimpleDrawView) {
	tabbedPane.setSelectedIndex(i);
	SimpleDrawView v = (SimpleDrawView) comp;
	setDocView(v.getSimpleDrawDocument(), v);
	v.setDrawToolkit(toolkit);
	v.activateView();
	// docChangeListener.reset();
	// v.refresh();
	setFrameTitle();
      }
    }
  }

  public void showViewTab(SimpleDrawDocument doc) {
    int i = findViewTab(doc);
    if (i >= 0) {
      showViewTab(i);
    }
  }

  public SimpleDrawView getViewTabAt(int i) { 
    if (tabbedPane != null) {
      Component comp = tabbedPane.getComponentAt(i);
      if (comp instanceof SimpleDrawView) {
	return (SimpleDrawView) comp;
      }
    }
    return null;
  }

  public void updateTabTitle(SimpleDrawDocument doc) {
    if (doc != null) {
      int i = findViewTab(doc);
      if (i >= 0) {
	tabbedPane.setTitleAt(i, doc.getName());
      }
    }
  }

  public void setTabIcon(int i, Icon icon) { 
    if (tabbedPane != null) {
      System.out.println("Main.setTabIcon() icon=" + icon); 
      tabbedPane.setIconAt(i, icon);
    }
  }

  protected abstract void setDocView(SimpleDrawDocument doc, SimpleDrawView view);

  public void removeView(SimpleDrawView view2) {
    if (view2 != null && tabbedPane != null) {
      tabbedPane.remove(view2);
      Component comp = tabbedPane.getSelectedComponent();
      if (comp instanceof SimpleDrawView) {
	SimpleDrawView view = (SimpleDrawView) comp;
	tabbedViewChanged(view);
      }
    }
  }

  public void removeViewTabAt(int index) {
    if (tabbedPane != null) {
      tabbedPane.removeTabAt(index);
      Component comp = tabbedPane.getSelectedComponent();
      if (comp instanceof SimpleDrawView) {
	SimpleDrawView view = (SimpleDrawView) comp;
	tabbedViewChanged(view);
      }
    }
  }

  public void displayMessage(String text,  Object... args) {
    String message = text; 
    if (args != null) { 
      message = MessageFormat.format(text, args);
    } 
    displayMessage(message);
  }

  public void displayMessage(String text) {
    if (messageLabel != null) {
      if (text != null) {
	messageLabel.setText(" " + text);
      } else {
	messageLabel.setText("");
      }
      //synchronous update message label 
      Dimension dim = messageLabel.getSize();
      messageLabel.paintImmediately(0, 0, dim.width, dim.height);
    }
  }

  static protected DecimalFormat f = new DecimalFormat("####0.00");

  public void displayLocation(float x, float y) {
    if (locationLabel != null) {
      locationLabel.setText("  (" + f.format(x) + ", " + f.format(y) + ")  ");
    }
  }

  public void displayLocation(int x, int y) {
    if (locationLabel != null) {
      locationLabel.setText("  (" + x + ", " + y + ")  ");
    }
  }

  public void enableHelpMessage(boolean b) {
    helpMessageEnabled = b;
  }

  protected String lastHelpKey = null;

  protected boolean helpMessageEnabled = true;

  public void displayHelpMessage(String key,  Object... args) {
    if (helpMessageEnabled && 
	key != null) { 
      String message = getResourceString(key);
      displayMessage(message, args);
    }
  }

  public void displayHelpMessage(String key) {
    if (helpMessageEnabled && 
	key != null && 
	!key.equals(lastHelpKey)) {
      displayMessage(getResourceString(key));
      lastHelpKey = key;
    }
  }

  public void startBusyMessage(String key) {
    if (key != null) { 
      displayHelpMessage(key);
    }
    hourGlassOn();
  }

  public void endBusyMessage(String key) {
    hourGlassOff();
    if (key != null) { 
      displayHelpMessage(key);
    }
  }

  public JComponent getWorkArea() {
    if (canvasPanel instanceof JXTitledPanel) { 
      return (JComponent) ((JXTitledPanel) canvasPanel).getContentContainer();
    } else {
      return canvasPanel;
    }
  }

  protected abstract void tabbedViewChanged(SimpleDrawView view);

  public String getName() {
    return name;
  }

  public JComponent getAppComponent(){
    return top;
  }

  public JComponent getAppToolBar(){
    return toolbar;
  }

  public JMenuBar getAppMenuBar(){
    return menuBar;
  }

  public JComponent getAppStatusBar(){
    return statusBar;
  }

  public JTabbedPane getTabbedPane() { 
    return tabbedPane;
  }

  public static Map<String, Icon> getIcons() {
    return icons;
  }

  public static void addIcon(String name, Icon icon) {
    if (name != null && icon != null) { 
      icons.put(name, icon);
    }
  }

  public static Icon getIcon(String name) {
    if (name != null) {
      return icons.get(name);
    }
    return null;
  }

  protected Main(BaseApplicationFrame frame, String name) {
    super(frame);
    this.name = name; 
  }

  protected String name; 

  protected JComponent top;

  protected JComponent statusBar = null;
  protected Component[] statusBarLeft = null;
  protected JComponent  statusBarRight = null;

  protected JLabel messageLabel = null;
  protected JLabel locationLabel = null;
  protected JTabbedPane tabbedPane = null;
  protected JComponent canvasPanel = null; 

  protected JComponent toolbar = null;
  protected JMenuBar menuBar = null;
  
  protected int dividerSize = 4;
  protected boolean titledPanels = false; 
  protected boolean shadowPanels = false; 

  protected int panelWidth[]       = { 150, 500, 150 };
  protected int upperPanelHeight[] = { 400, 400, 400 };

  protected static Map<String, Icon> icons = new HashMap<String, Icon>();

  class TabbedPaneListener implements ChangeListener {

    public void stateChanged(ChangeEvent e) {

      // System.out.println("TabbedPaneListener.stateChanged()");

      Component comp = tabbedPane.getSelectedComponent();
      if (comp instanceof SimpleDrawView) {
	SimpleDrawView view = (SimpleDrawView) comp;
	tabbedViewChanged(view);
      }
    }

  }

}
